package com.spring.framework.transaction.example;

import com.spring.framework.transaction.model.TxStudy;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * @author xuweizhi
 * @since 2021/08/03 10:27
 */
@Component
@SuppressWarnings("all")
public class TxStudyMapper {

	/**
	 * jdbc模版，用于操作数据库
	 */
	@Resource
	private JdbcTemplate jdbcTemplate;

	/**
	 * 事务模版，用于操作事务
	 */
	@Resource
	private TransactionTemplate transactionTemplate;

	/**
	 * 事务管理器
	 */
	@Resource
	private DataSourceTransactionManager transactionManager;


	public void transactionTemplateTest() {
		//transactionTemplate的execute方法便捷的处理事务
		Integer execute = transactionTemplate.execute(status -> {
			//在doInTransaction方法中调用业务代码，并且支持返回值
			return jdbcOperate();
		});
		System.out.println(execute);
	}

	/**
	 * 业务操作
	 */
	private int jdbcOperate() {
		//插入的sql
		String sql = "insert into tx_study (name,age) values (?,?)";
		//调用jdbcTemplate的update方法，插入数据
		return jdbcTemplate.update(sql, "insert", 20);
	}

	/**
	 * 回滚测试
	 */
	public void tpExTest() {
		//transactionTemplate的execute方法便捷的处理事务
		transactionTemplate.execute(status -> {
			//在doInTransaction方法中调用业务代码，并且支持返回值
			return jdbcOperateEx();
		});
	}

	/**
	 * 业务操作，模拟抛出非受检异常
	 */
	private int jdbcOperateEx() {
		//插入的sql
		String sql = "insert into tx_study (name,age) values (?,?)";
		//调用jdbcTemplate的update方法，插入数据
		int insert = jdbcTemplate.update(sql, "insert2", 21);

		//手动制造一个非受检异常
		int i = 1 / 0;
		return insert;
	}

	/**
	 * 对于受检异常，实际上使用该方法时不允许直接抛出受检异常，而是必须处理！
	 *
	 * 此时我们可以使用doInTransaction方法传递的参数TransactionStatus，这个就是当前事务对象，我们可以调用它的setRollbackOnly方法
	 * 手动设该事务的唯一结果就回滚，用来代替抛出异常
	 */
	public void tpCheckExTest() {
		//transactionTemplate的execute方法便捷的处理事务
		Integer execute = transactionTemplate.execute(status -> {
			//受检异常必须处理，否则编译不通过
			try {
				return jdbcOperateCheckEx();
			} catch (IOException e) {
				//调用setRollbackOnly方法回滚事务
				status.setRollbackOnly();
				return null;
			}
		});
		System.out.println(execute);
	}

	/**
	 * 业务操作，模拟抛出非受检异常
	 */
	private int jdbcOperateCheckEx() throws IOException {
		//插入的sql
		String sql = "insert into tx_study (name,age) values (?,?)";
		//调用jdbcTemplate的update方法，插入数据
		int insert = jdbcTemplate.update(sql, "insert2", 21);
		//手动抛出一个受检异常
		throw new IOException();
	}

	/**
	 * 如果不需要返回值，那么可以使用executeWithoutResult方法
	 */
	public void tpExecuteWithoutResult() {
		//transactionTemplate的executeWithoutResult方法便捷的处理不需要返回值的事务操作
		transactionTemplate.executeWithoutResult(status -> jdbcOperateSelect());
	}

	/**
	 * 业务操作，没有返回值
	 */
	private void jdbcOperateSelect() {
		//查询的sql
		String sql = "select * from tx_study where id = ?";
		TxStudy txStudy = jdbcTemplate.queryForObject(sql, BeanPropertyRowMapper.newInstance(TxStudy.class), 1);
		System.out.println(txStudy);
	}

	public void tmTest() {
		//创建一个事务定义对象，可以设置事务的属性
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		//只能以编程方式执行时才能显式设置事务名称
		def.setName("SomeTxName");
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		//通过getTransaction方法获取一个事务，此时已经开启了事务
		TransactionStatus transaction = transactionManager.getTransaction(def);

		//调用业务方法
		try {
			jdbcOperateTm();
		} catch (Exception e) {
			e.printStackTrace();
			//需要手动回滚事务
			transactionManager.rollback(transaction);
		}

		//需要手动提交事务
		transactionManager.commit(transaction);
	}

	/**
	 * 业务操作
	 */
	private void jdbcOperateTm() {
		//插入的sql
		String sql = "insert into tx_study (name,age) values (?,?)";
		//调用jdbcTemplate的update方法，插入数据
		int insert = jdbcTemplate.update(sql, "transactionManager1", 22);
		System.out.printf(1/0+"");
	}



}
